﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using InHouse.SharedLibraries.DataContext;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.Tools.DataGenerator;
using Microsoft.Data.Schema.Tools.DataGenerator.Generators;
using Microsoft.Data.Schema.Tools.Settings;
namespace InHouse.TFSExtensions.DBDataGenerators
{
    [System.ComponentModel.DisplayName("Static Data Generator")]
    [System.ComponentModel.Description("The Static Data Generator loads data from a table and populates the target table with exact replicas of the data.")]
    [DatabaseSchemaProviderCompatibility(typeof(DatabaseSchemaProvider))]
    [Generator(typeof(StaticDataGeneratorDesigner))]
    [GeneratorStyles(DesignerStyles = GeneratorDesignerStyles.CanProduceUniqueValues | GeneratorDesignerStyles.Deterministic)]
    public class StaticDataGenerator : Generator
    {
        public static Dictionary<string,DataTable> _DataTables = new Dictionary<string,DataTable>();
        public static int _ReferenceCount = 0;
        private int _RowNumber = -1;
        
        private static Dictionary<string,object>_LockObjects = new Dictionary<string,Object>();
        [Input(Name = "Connection String", Description = "The connection string to the target database containing the static data.")]
        public string ConnectionString
        {
            get;
            set;
        }


        private string UniqueKey()
        {
            return ConnectionString + TableName;
        }

        //Have an input parameter of the table name automatically set to the selected table.
        private string _TableName;
        [Input(Name = "Table Name", Description = "The name of the table you wish to use as the source of static data.",
            AutoAssignedInput=AutoAssignedInput.TableName)]
        public string TableIdentifier
        {
             get {return _TableName;}
            set { _TableName = value; }
        }

        protected override void OnGenerateNextValues()
        {
            _RowNumber++;
        }

        protected override object OnGetOutputValue()
        {
            return _DataTables[UniqueKey()].Rows[_RowNumber][this.ColumnName];
        }

        private void LoadData()
        {
            
            //Quick check with no locks to see if data has been loaded.
            if (!_DataTables.ContainsKey(UniqueKey()))
            {
                //Looks like no data is loaded

                //Do we have a lock object for this table?
                if (!_LockObjects.ContainsKey(UniqueKey()))
                {
                    //No, lock the dictionary so that only one thread can create a locking object
                    lock(_LockObjects)
                    {
                        if (!_LockObjects.ContainsKey(UniqueKey()))
                            _LockObjects.Add(UniqueKey(),new object());
                    }
                }

                lock (_LockObjects[UniqueKey()])
                {
                    //Only one thread can be here, check to make sure some other thread did not already load the data.
                    if (!_DataTables.ContainsKey(UniqueKey()))
                    {
                        //No data is loaded so get it loaded.
                        string sqlTemplate = "select top {0} * from {1}";
                        DataContext dc = new DataContext(ConnectionString);
                        
                        _DataTables[UniqueKey()] = dc.FillDataTable(string.Format(sqlTemplate,
                                this.RowsToInsert,this.TableName), null);
                        if (_DataTables[UniqueKey()] == null)
                        {
                            throw new DataNotLoadedException(
                                string.Format("Failed to load the data from {1} using connection string {2}",
                                    this.TableName,
                                    this.ConnectionString));
                        }
                    }
                }
            }
        }

        protected override void OnInitialize(GeneratorInit initInfo)
        {
            base.OnInitialize(initInfo);
            LoadData();
            _ReferenceCount++;
        }

        protected override void Dispose(bool disposing)
        {
            
            _ReferenceCount--;
            if (_ReferenceCount <= 0)
            {
                _ReferenceCount = 0;
                _DataTables.Clear();
            }

            base.Dispose(disposing);
        }
    }

    [Serializable]
    public class DataNotLoadedException : Exception
    {
        public DataNotLoadedException() { }
        public DataNotLoadedException(string message) : base(message) { }
        public DataNotLoadedException(string message, Exception inner) : base(message, inner) { }
        protected DataNotLoadedException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }





    

}
